package com.ruihu.admin.module.business.stat.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.ruihu.admin.common.domain.ResponseDTO;
import com.ruihu.admin.module.business.electric.config.domain.dto.ElectricConfigDTO;
import com.ruihu.admin.module.business.electric.config.service.ElectricConfigService;
import com.ruihu.admin.module.business.stat.constant.DateSelectTypeEnum;
import com.ruihu.admin.module.business.stat.dao.StatDao;
import com.ruihu.admin.module.business.stat.domain.dto.StatQueryDTO;
import com.ruihu.admin.module.business.stat.domain.vo.*;
import com.ruihu.admin.module.business.tasknode.dao.DefectTypeDao;
import com.ruihu.admin.module.business.tasknode.domain.entity.DefectTypeEntity;
import com.ruihu.admin.util.RhDateUtil;
import com.ruihu.admin.util.RhLocalDateTimeUtil;
import com.ruihu.admin.util.RhNumberUtil;
import org.apache.commons.collections4.SetUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class StatService {

    @Autowired
    private StatDao statDao;

    @Autowired
    private DefectTypeDao defectTypeDao;

    @Autowired
    private ElectricConfigService electricConfigService;

    /**
     * 业务指标
     * @return
     */
    public ResponseDTO<List<ElectricCoreCountVO>> queryKPI() {
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<ElectricCoreCountVO> businessKpiList = new ArrayList<>(6);
        // 今日拆解数量
        Date todayBegin = RhLocalDateTimeUtil.beginOfTodayToDate(currentDateTime);
        Date todayEnd = RhLocalDateTimeUtil.endOfTodayToDate(currentDateTime);
        Integer disassembleCountOfToday = this.statDao.countDisassembleElectricCore(todayBegin, todayEnd);
        Integer disassembleCountOfYesterday = this.statDao.countDisassembleElectricCore(DateUtil.offsetDay(todayBegin, -1), DateUtil.offsetDay(todayEnd, -1));
        String growRate = RhNumberUtil.growRateInPercentWithSign(NumberUtil.toDouble(disassembleCountOfToday - disassembleCountOfYesterday), NumberUtil.toDouble(disassembleCountOfYesterday));
        ElectricCoreCountVO electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("今日拆解电芯数量")
                .count(disassembleCountOfToday)
                .icon("md-locate")
                .color("#11A0F8")
                .percent("环比昨日" + growRate)
                .isUpOrDown(CompareUtil.compare(disassembleCountOfToday, disassembleCountOfYesterday) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        // 本周拆解数量
        Date firstDayOfThisWeek = RhLocalDateTimeUtil.firstDayOfThisWeekToDate(currentDateTime);
        Date lastDayOfThisWeek = RhLocalDateTimeUtil.lastDayOfThisWeekToDate(currentDateTime);
        Date firstDayOfLastWeek = RhLocalDateTimeUtil.firstDayOfLastWeekToDate(currentDateTime);
        Date lastDayOfLastWeek = RhLocalDateTimeUtil.lastDayOfLastWeekToDate(currentDateTime);
        Integer disassembleCountOfThisWeek = this.statDao.countDisassembleElectricCore(firstDayOfThisWeek, lastDayOfThisWeek);
        Integer disassembleCountOfLastWeek = this.statDao.countDisassembleElectricCore(firstDayOfLastWeek, lastDayOfLastWeek);
        growRate = RhNumberUtil.growRateInPercentWithSign(NumberUtil.toDouble(disassembleCountOfThisWeek - disassembleCountOfLastWeek), NumberUtil.toDouble(disassembleCountOfLastWeek));
        electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("本周拆解电芯数量")
                .count(disassembleCountOfThisWeek)
                .icon("md-locate")
                .color("#FFBB44")
                .percent("环比上周" + growRate)
                .isUpOrDown(CompareUtil.compare(disassembleCountOfThisWeek, disassembleCountOfLastWeek) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        // 本月拆解数量
        Date firstDayOfThisMonth = RhLocalDateTimeUtil.firstDayOfThisMonthToDate(currentDateTime);
        Date lastDayOfThisMonth = RhLocalDateTimeUtil.lastDayOfThisMonthToDate(currentDateTime);
        Date firstDayOfLastMonth = RhLocalDateTimeUtil.firstDayOfLastMonthToDate(currentDateTime);
        Date lastDayOfLastMonth = RhLocalDateTimeUtil.lastDayOfLastMonthToDate(currentDateTime);
        Integer disassembleCountOfThisMonth = this.statDao.countDisassembleElectricCore(firstDayOfThisMonth, lastDayOfThisMonth);
        Integer disassembleCountOfLastMonth = this.statDao.countDisassembleElectricCore(firstDayOfLastMonth, lastDayOfLastMonth);
        growRate = RhNumberUtil.growRateInPercent(NumberUtil.toDouble(disassembleCountOfThisMonth - disassembleCountOfLastMonth), NumberUtil.toDouble(disassembleCountOfLastMonth));
        electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("本月拆解电芯数量")
                .count(disassembleCountOfThisMonth)
                .icon("md-help-circle")
                .color("#7ACE4C")
                .percent("环比上月" + growRate)
                .isUpOrDown(CompareUtil.compare(disassembleCountOfThisMonth, disassembleCountOfLastMonth) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        // 今日拆解电芯合格数量
        Integer eligibleDisassembleCountOfToday = this.statDao.countEligibleDisassembleElectricCore(todayBegin, todayEnd);
        Integer eligibleDisassembleCountOfYesterday = this.statDao.countEligibleDisassembleElectricCore(DateUtil.offsetDay(todayBegin, -1), DateUtil.offsetDay(todayEnd, -1));
        growRate = RhNumberUtil.growRateInPercent(NumberUtil.toDouble(eligibleDisassembleCountOfToday - eligibleDisassembleCountOfYesterday), NumberUtil.toDouble(eligibleDisassembleCountOfYesterday));
        electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("今日拆解合格电芯数量")
                .count(eligibleDisassembleCountOfToday)
                .icon("md-share")
                .color("#11A0F8")
                .percent("环比昨日" + growRate)
                .isUpOrDown(CompareUtil.compare(eligibleDisassembleCountOfToday, eligibleDisassembleCountOfYesterday) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        // 本周拆解电芯合格数量
        Integer eligibleDisassembleCountOfThisWeek = this.statDao.countEligibleDisassembleElectricCore(firstDayOfThisWeek, lastDayOfThisWeek);
        Integer eligibleDisassembleCountOfLastWeek = this.statDao.countEligibleDisassembleElectricCore(firstDayOfLastWeek, lastDayOfLastWeek);
        growRate = RhNumberUtil.growRateInPercent(NumberUtil.toDouble(eligibleDisassembleCountOfThisWeek - eligibleDisassembleCountOfLastWeek), NumberUtil.toDouble(eligibleDisassembleCountOfLastWeek));
        electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("本周拆解合格电芯数量")
                .count(eligibleDisassembleCountOfThisWeek)
                .icon("md-chatbubbles")
                .color("#91AFC8")
                .percent("环比上周" + growRate)
                .isUpOrDown(CompareUtil.compare(eligibleDisassembleCountOfThisWeek, eligibleDisassembleCountOfLastWeek) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        // 本月拆解电芯合格数量
        Integer eligibleDisassembleCountOfThisMonth = this.statDao.countEligibleDisassembleElectricCore(firstDayOfThisMonth, lastDayOfThisMonth);
        Integer eligibleDisassembleCountOfLastMonth = this.statDao.countEligibleDisassembleElectricCore(firstDayOfLastMonth, lastDayOfLastMonth);
        growRate = RhNumberUtil.growRateInPercent(NumberUtil.toDouble(eligibleDisassembleCountOfThisMonth - eligibleDisassembleCountOfLastMonth), NumberUtil.toDouble(eligibleDisassembleCountOfLastMonth));
        electricCoreCountVO = ElectricCoreCountVO.builder()
                .title("本月拆解合格电芯数量")
                .count(eligibleDisassembleCountOfThisMonth)
                .icon("md-map")
                .color("#F47F92")
                .percent("环比上月" + growRate)
                .isUpOrDown(CompareUtil.compare(eligibleDisassembleCountOfThisMonth, eligibleDisassembleCountOfLastMonth) > 0)
                .build();
        businessKpiList.add(electricCoreCountVO);

        return ResponseDTO.succData(businessKpiList);
    }


    /**
     * 查询拆解及不良数趋势图
     * @param statQueryDTO
     * @return
     */
    public ResponseDTO<ChartLineVO> queryDisassembleAndEligibleCount(StatQueryDTO statQueryDTO) {
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<Date> recentDays;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(statQueryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneMonthDays(currentDateTime);
        } else {
            recentDays = RhLocalDateTimeUtil.recent7Days(currentDateTime);
        }

        Date beginDate = recentDays.get(0);
        Date endDate = recentDays.get(recentDays.size() - 1);
        LinkedHashMap<String, String> daysOfRangeMap;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(statQueryDTO.getDateType())) {
            daysOfRangeMap = RhDateUtil.getDayOfOneMonthTheseDays(beginDate, endDate);
        } else {
            daysOfRangeMap = RhDateUtil.getDayOfWeekTheseDays(beginDate, endDate);
        }
        endDate = DateUtil.endOfDay(endDate).toJdkDate();
        // 统计拆解数
        List<Map<String, Object>> disassembleCountListPart = this.statDao.groupCountDisassembleElectricCore(beginDate, endDate);
        // 统计不良数
        List<Map<String, Object>> ineligibleDisassembleListPart = this.statDao.groupCountIneligibleDisassembleElectricCore(beginDate, endDate);

        List<String> dateRangeList = ListUtil.toList(daysOfRangeMap.values());
        List<Map<String, Object>> disassembleCountList = this.createBaseDataList(daysOfRangeMap);
        List<Map<String, Object>> ineligibleDisassembleList = this.createBaseDataList(daysOfRangeMap);
        // 数据库查询数据不全，则补齐日期期间的数据
        this.supplementData(disassembleCountList, disassembleCountListPart);
        this.supplementData(ineligibleDisassembleList, ineligibleDisassembleListPart);

        List<ChartLineSerie<Object>> chartLineSerieList = new ArrayList<>();
        ChartLineSerie<Object> chartLineSerie = ChartLineSerie.builder()
                .name("拆解数")
                .data(disassembleCountList.stream().map(e -> e.get("cnt")).collect(Collectors.toList()))
                .build();
        chartLineSerieList.add(chartLineSerie);
        chartLineSerie = ChartLineSerie.builder()
                .name("不良数")
                .data(ineligibleDisassembleList.stream().map(e -> e.get("cnt")).collect(Collectors.toList()))
                .build();
        chartLineSerieList.add(chartLineSerie);

        ChartLineVO chartLineVO = new ChartLineVO();
        chartLineVO.setLegendList(ListUtil.toList("拆解数", "不良数"));
        chartLineVO.setXAxisData(dateRangeList);
        chartLineVO.setChartLineSerieList(chartLineSerieList);

        return ResponseDTO.succData(chartLineVO);
    }

    /**
     * 电芯型号占比数据
     * @param queryDTO
     * @return
     */
    public ResponseDTO<ElectricCoreTypeCountVO> countByElectricCoreType(StatQueryDTO queryDTO) {
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<Date> recentDays;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(queryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneMonthDays(currentDateTime);
        } else if (DateSelectTypeEnum.LAST_ONE_YEAR.getValue().equals(queryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneYearDays(currentDateTime);
        } else {
            recentDays = RhLocalDateTimeUtil.recent7Days(currentDateTime);
        }

        Date beginDate = recentDays.get(0);
        Date endDate = recentDays.get(recentDays.size() - 1);
        endDate = DateUtil.endOfDay(endDate).toJdkDate();
        // 统计拆解总数
        Integer disassembleCount = this.statDao.countDisassembleElectricCore(beginDate, endDate);
        // 按型号统计
        List<Map<String, Object>> disassembleCountByType = this.statDao.countElectricCoreByType(beginDate, endDate);
        disassembleCountByType.forEach(e -> {
            if (!e.containsKey("name"))
                e.put("name", "空");
            else {
                ResponseDTO<ElectricConfigDTO> resp = this.electricConfigService.getElectricConfigByCode((String) e.get("name"));
                if (resp.getData() != null) {
                    e.put("name", resp.getData().getType());
                }
            }
        });
        ElectricCoreTypeCountVO countVO = ElectricCoreTypeCountVO.builder()
                .disassembleCount(disassembleCount)
                .data(disassembleCountByType)
                .build();
        return ResponseDTO.succData(countVO);
    }

    /**
     * 电芯型号占比数据
     * @param queryDTO
     * @return
     */
    public ResponseDTO<DefectTop5VO> countTop5ByDefect(StatQueryDTO queryDTO) {
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<Date> recentDays;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(queryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneMonthDays(currentDateTime);
        } else if (DateSelectTypeEnum.LAST_ONE_YEAR.getValue().equals(queryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneYearDays(currentDateTime);
        } else {
            recentDays = RhLocalDateTimeUtil.recent7Days(currentDateTime);
        }

        Date beginDate = recentDays.get(0);
        Date endDate = recentDays.get(recentDays.size() - 1);
        endDate = DateUtil.endOfDay(endDate).toJdkDate();
        // 统计缺陷总数
        Integer defectCount = this.statDao.countDefectType(beginDate, endDate);
        // 按型号统计
        List<Map<String, Object>> countTop5DefectType = this.statDao.countTop5DefectType(beginDate, endDate);
        List<ProcessDataVO> processDataVOList = new ArrayList<>(countTop5DefectType.size());
        for (Map<String, Object> map : countTop5DefectType) {
            Long count = 0L;
            String defectName = "空";
            if (map.get("defect_name") != null)
                defectName = (String) map.get("defect_name");
            if (map.get("cnt") != null)
                count = (Long) map.get("cnt");
            ProcessDataVO processDataVO = ProcessDataVO.builder()
                    .name(defectName)
                    .value(count.intValue())
                    .color("#3AA1FF")
                    .percent(NumberUtil.div(count, defectCount, 2).doubleValue() * 100)
                    .build();
            processDataVOList.add(processDataVO);
        }
        DefectTop5VO defectTop5VO = DefectTop5VO.builder()
                .defectCount(defectCount)
                .processDataList(processDataVOList)
                .build();
        return ResponseDTO.succData(defectTop5VO);
    }

    /**
     * 电芯缺陷分类统计
     * @param statQueryDTO
     * @return
     */
    public ResponseDTO<ChartLineVO> queryElectricCoreDefectTypeCount(StatQueryDTO statQueryDTO) {
        LocalDateTime currentDateTime = LocalDateTime.now();
        List<Date> recentDays;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(statQueryDTO.getDateType())) {
            recentDays = RhLocalDateTimeUtil.recentOneMonthDays(currentDateTime);
        } else {
            recentDays = RhLocalDateTimeUtil.recent7Days(currentDateTime);
        }

        List<String> defectTypeList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(statQueryDTO.getDefectTypeIdList())) {
            List<DefectTypeEntity> defectTypeEntityList = this.defectTypeDao.selectBatchIds(statQueryDTO.getDefectTypeIdList());
            defectTypeEntityList.stream().forEach(e -> {
                defectTypeList.add(e.getDefectName());
            });
        }

        // 统计拆解数
        Date beginDate = recentDays.get(0);
        Date endDate = recentDays.get(recentDays.size() - 1);
        endDate = DateUtil.endOfDay(endDate).toJdkDate();
        LinkedHashMap<String, String> daysOfRangeMap;
        if (DateSelectTypeEnum.LAST_THIRTY_DAYS.getValue().equals(statQueryDTO.getDateType())) {
            daysOfRangeMap = RhDateUtil.getDayOfOneMonthTheseDays(beginDate, endDate);
        } else {
            daysOfRangeMap = RhDateUtil.getDayOfWeekTheseDays(beginDate, endDate);
        }
        // 缺陷分类统计数据
        List<Map<String, Object>> countByDefectType = this.statDao.countElectricCoreByDefectType(beginDate, endDate, statQueryDTO.getDefectTypeIdList());
        // 创建图形系列
        List<ChartLineSerie<Object>> chartLineSerieList = new ArrayList<>(defectTypeList.size());
        for (String defectType : defectTypeList) {
            List<Map<String, Object>> theDefectTypeCountList = countByDefectType.stream()
                    .filter(e -> defectType.equals(e.get("defect_name")))
                    .collect(Collectors.toList());

            // 数据列，按照日期排序
            List<Map<String, Object>> dataList = this.createBaseDataList(daysOfRangeMap);
            if (theDefectTypeCountList.size() != dataList.size()) {
                this.supplementData(dataList, theDefectTypeCountList);
            }

            ChartLineSerie<Object> chartLineSerie = ChartLineSerie
                    .builder()
                    .name(defectType)
                    .data(dataList.stream().map(e -> e.get("cnt")).collect(Collectors.toList()))
                    .build();
            chartLineSerieList.add(chartLineSerie);
        }

        List<String> dateRangeList = ListUtil.toList(daysOfRangeMap.values());
        ChartLineVO chartLineVO = new ChartLineVO();
        chartLineVO.setLegendList(defectTypeList);
        chartLineVO.setXAxisData(dateRangeList);
        chartLineVO.setChartLineSerieList(chartLineSerieList);

        return ResponseDTO.succData(chartLineVO);
    }

    private List<Map<String, Object>> createBaseDataList(LinkedHashMap<String, String> daysOfRangeMap) {
        List<Map<String, Object>> baseDataList = new ArrayList<>(daysOfRangeMap.size());
        for (Map.Entry<String, String> entry : daysOfRangeMap.entrySet()) {
            Map<String, Object> data = new HashMap<>();
            data.put("date", entry.getKey());
            data.put("cnt", 0);
            baseDataList.add(data);
        }

        return baseDataList;
    }

    /**
     * 以baseDataList数据列表为基础，补齐数据
     * @param baseDataList
     * @param partDataList
     */
    private void supplementData(List<Map<String, Object>> baseDataList, List<Map<String, Object>> partDataList) {
        int i = 0;
        int j = 0;
        for (; i < baseDataList.size(); i++) {
            String date = (String) baseDataList.get(i).get("date");
            // disassembleCountListPart 按日期排好序
            if (j < partDataList.size()) {
                String d = (String) partDataList.get(j).get("date");
                if (StrUtil.compare(date, d, false) == 0) {
                    baseDataList.get(i).put("cnt", partDataList.get(j).get("cnt"));
                    j++;
                }
            }
        }
    }
}
